#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<cstdio>
#include<ctype.h>
#include<fcntl.h>
#include<sys/stat.h>
using namespace std;
const int basesize = 1024; 
const int argvnum = 64;
const int envnum = 64;
//全局的命令行参数
char* gargv[argvnum];
int gargc = 0;

//我的系统的环境变量
char* genv[envnum];
//全局的当前shell的工作路径
char pwd[basesize]; 
char pwdenv[basesize];
//全局变量
int lastcode = 0;
//与重定向有关的全局变量
#define NoneRedir 1
#define InputRedir 2
#define OutputRedir 3
#define AppRedir 4

#define TrimSpace(pos) do{\
   while(isspace(*pos)){\
       pos++;\
   }\
}while(0)

int redir = NoneRedir;
char* filename = nullptr;
string GetUserName()
{
    string name = getenv("USER");
    return name.empty() ? "None" : name;
}

string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

string GetPwd()
{
    //string pwd = getenv("PWD");
    if(nullptr == getcwd(pwd,sizeof(pwd))) return "None";
    
    snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
    putenv(pwdenv);//维护自己的环境变量
    return pwd;
    //return pwd.empty() ? "None" : pwd;
}

string LastDir()
{
    string curr = GetPwd();
    if(curr == "/" || curr == "None")return curr;

    size_t pos = curr.rfind("/");
    return curr.substr(pos+1); 
}
string MakeCommandLine()
{
    char command_line[basesize];
    snprintf(command_line,basesize,"[%s@%s %s]# ",GetUserName().c_str(),GetHostName().c_str(),LastDir().c_str());

    return command_line;
}

void PrintCommandLine()
{
    printf("%s",MakeCommandLine().c_str());
    fflush(stdout);
}

bool GetCommandLine(char command_buffer[],int basesize)
{
    //我们认为，我们要将用户输入的命令行，当作一个完整的字符串
    char* result = fgets(command_buffer,basesize,stdin);
    if(!result)
    {
        return false;
    }
    command_buffer[strlen(command_buffer)-1] = 0;
    if(strlen(command_buffer)== 0)
        return false;

    return true;
}

bool ParseCommandLine(char command_buffer[],int size)//3,分析命令
{
    (void)size;
    memset(gargv,0,sizeof(gargv));
    gargc = 0;

    //重定向 每一行命令都要初始化状态
    redir = NoneRedir;
    filename = nullptr;

    int end = size -1;
    while(end>=0)
    {
        if(command_buffer[end] == '<')//输入重定向
        {
            redir = InputRedir;
            filename = &command_buffer[end] + 1;
            TrimSpace(filename);
            command_buffer[end] = 0;
            break;
        }
        else if(command_buffer[end] == '>')//输出重定向
        {
            if(command_buffer[end-1] == '>')//追加重定向
            {
                redir = AppRedir;
                filename = &command_buffer[end] +1;
                command_buffer[end] = 0;
                command_buffer[end-1] = 0;
                TrimSpace(filename);
                break;
            }
            else 
            {
                redir = OutputRedir;
                command_buffer[end] = 0;
                filename = &command_buffer[end] +1;
                TrimSpace(filename);
                break;
            }
        }
        else 
        {
            end--;
        }
    }
    const char* sep = " ";
    gargv[gargc++] = strtok(command_buffer,sep);
    //=是刻意写的
    while((bool)(gargv[gargc++] = strtok(nullptr,sep)));
    gargc--;

    return true;

}
//在shell中
//有些命令，必须由子进程来执行
//有些命令，不能由子进程来执行，要由shell自己执行 -------内建命令 built command
bool ExecutrCommand()  //4,执行命令
{
    //让子进程执行
    pid_t id = fork();
    if(id < 0) return false;
    if(id == 0)
    {
       if(redir == InputRedir) 
       {
           if(filename)
           {

             int fd = open(filename,O_RDONLY);
             if(fd<0)
             {
                 exit(2);
             }
             dup2(fd,0);

           }
           else 
           {
               exit(1);
           }
       }
       else if(redir == OutputRedir)
       {
           if(filename)
           {
             int fd = open(filename,O_CREAT | O_WRONLY | O_TRUNC ,0666);
             if(fd<0)
             {
                 exit(4);
             }
             dup2(fd,1);
           }
           else 
           {
               exit(3);
           }
       }
       else if(redir == AppRedir)
       {
           if(filename)
           {
             int fd = open(filename,O_CREAT | O_WRONLY | O_APPEND,0666);
             if(fd<0)
             {
                 exit(6);
             }
             dup2(fd,1);
           }
           else 
           {
               exit(5);
           }
       }
       
       execvpe(gargv[0],gargv,genv);//把自己的命令行参数表和环境变量表传给子进程
       
       exit(1);
    }

    int status =0;
    pid_t rid = waitpid(id,&status,0);
    if(rid > 0)
    {
        if(WIFEXITED(status))
        {
            lastcode = WEXITSTATUS(status);
        }
        else 
        {
            lastcode = 100;
        }
        return true;
    }

    return false;
}

void AddEnv(const char* item)
{
    int index = 0;
    while(genv[index])
    {
        index++;
    }
    //不能直接把item给genv[index]，因为item存的是一个字符串的地址,和gargv[1]里的内容相同，而gargv[1]的内容来自command_buffer,所以item指向的是command_buffer中的一部分，但是command在一次循环之后就会被销毁
    //第二次循环再创建，此时原来里面的值也就销毁了，所以这时item就是一个野指针，所以不能单纯的将item赋值给gargcp[index],必须malloc，然后深拷贝
    genv[index] = (char*)malloc(strlen(item) +1);
    strncpy(genv[index],item,strlen(item)+1);
    genv[++index] = nullptr;
}
//执行内建命令
bool CheckAndExecBuiltCommand()
{
    if(strcmp(gargv[0],"cd") == 0)
    {
        if(gargc == 2)
        {
            chdir(gargv[1]);
            lastcode = 0;

        }
        else
        {
            lastcode = 1;
        }
        return true;
    }
    else if(strcmp(gargv[0],"export") == 0)
    {
        if(gargc == 2)
        {
            AddEnv(gargv[1]);
            lastcode = 0;
        }
        else 
        {
            lastcode = 2;
        }
        return true;    
    }
    else if(strcmp(gargv[0],"env") == 0)
    {
        for(int i = 0;genv[i];i++)
        {
            printf("%s\n",genv[i]);
        }
        lastcode = 0;
        return true;
    }
    else if(strcmp(gargv[0],"echo") == 0)
    {
        if(gargc ==2 )
        {
            if(gargv[1][0] == '$')
            {
               if(gargv[1][1] == '?')
               {
                   printf("%d\n",lastcode);
                   lastcode = 0;
               }
            }
            else 
            {
                printf("%s\n",gargv[1]);//直接打印echo后面的字符串，但是只能打印一个
                lastcode = 0;
            }

        }
        else 
        {
            lastcode = 3;
        }
        return true;
    }
    return false;
}

void InitEnv()
{
    extern char** environ;
    int index = 0;
    while(environ[index])
    {
        genv[index] = (char*)malloc(strlen(environ[index])+1);
        strncpy(genv[index],environ[index],strlen(environ[index])+1);
        index++;
    }
    genv[index] = nullptr;
}
int main()
{
    InitEnv();
    while(true)
    {
    char command_buffer[basesize];
    PrintCommandLine(); //1,命令行提示符
    if( !GetCommandLine(command_buffer,basesize) )
    {
        continue;
    }//2,获取命令
   // printf("%s\n",command_buffer);
    ParseCommandLine(command_buffer,strlen(command_buffer)); //3,分析命令
    if(CheckAndExecBuiltCommand())
    {
        continue;
    }
    ExecutrCommand();  //4,执行命令
    }
    return 0;
}
